package core

import (
	"io/ioutil"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/russross/blackfriday"
	"gopkg.in/yaml.v2"
)

/*
 * 对存储html的一个数组变量
 */
type HTMLS []HTML

var htmls HTMLS

/*
 * 返回数组长度
 */
func (p HTMLS) Len() int {
	return len(p)
}

func (p HTMLS) Less(i, j int) bool {
	if p[i].Time.Unix() > p[j].Time.Unix() {
		return true
	}
	if p[i].Time.Unix() < p[j].Time.Unix() {
		return false
	}
	return false
}

func (p HTMLS) Swap(i, j int) {
	p[i], p[j] = p[j], p[i]
}

func (p *HTMLS) Sort() {
	sort.Sort(p)
}

/*
 * 设置页面YAML配置信息,在页面上配置的在该结构体中的数据可以正常识别,不包含的无法识别
 */
type PageInfo struct {
	Lang        string   `yaml:"lang"`
	Title       string   `yaml:"title"`
	KeyWords    string   `yaml:"keywords"`
	Description string   `yaml:"description"`
	Slide       bool     `yaml:"slide"`
	Style       []string `yaml:"style"`
	Ico         string   `yaml:"ico"`
	Script      []string `yaml:"script"`
}

/*
 * 原设计将MarkDonw读取出来后拼接成Html
 * 目前缺点过多,所以准备独立出来一个文件专门用来进行Html组装,直接将Markdown组装成HTML半成品
 * 好处,可以一文件一HTML,网页head配置比较方便读取,footer底部信息比较容易读取,容易加载三方样式库
 * 创建HTML类型,添加HTML类型子方法
 * 1.head中title,keywords,description,link,ico图标
 todo 2.body 主要解析Markdown文档,将markdown文档中的golang自定义标签转义好
 todo 3.将页面底部的script三方链接引入好配置到页面上const
 todo 4.将配置中的三方网站信息生成到footer中
*/

type HTML struct {
	IFile  string    // * Markdown文件路径位置
	OFile  string    // * HTML文件路径位置
	Router string    // * 网页相对路径地址
	Con    string    // * 网页内容
	Md     string    // * Markdown文件
	Title  string    // * 网页标题
	Time   time.Time // *文件更新时间
	HF     PageInfo  // * 页面YAML配置
	List   string
}

type HTMLBuiler struct {
	HTML
}

func (hb *HTMLBuiler) Create(i string) *HTMLBuiler {
	clear(hb)
	hb.HTML.IFile = i
	// * 读取文件内容  -> 读取页面YAML内容 -> 支持流程图扩展
	// * ->自定义插槽 -> 待做图标 -> toc目录 -> 配置检查,设置页面默认配置
	hb.readFile().readYAML().mermaid().slot().todo().html().list().toc().defaultConfig()
	return hb
}

/*
 * 设置导出位置路径
 */
func (hb *HTMLBuiler) SetOFile(o string) *HTMLBuiler {
	hb.HTML.OFile = o
	return hb
}

/*
 * 设置导出位置路径
 */
func (hb *HTMLBuiler) SetRouter(o string) *HTMLBuiler {
	hb.HTML.Router = strings.Replace(o[:len(o)-3]+".html", "README", "index", 1)
	return hb
}
func (hb *HTMLBuiler) WithTime(timeout time.Time) *HTMLBuiler {
	hb.HTML.Time = timeout
	return hb
}

func (sb *HTMLBuiler) Build() HTML {
	return sb.HTML
}

// - 读取markdown文档,挂载到HTML上
func (hb *HTMLBuiler) readFile() *HTMLBuiler {
	con, err := ioutil.ReadFile(hb.HTML.IFile)
	if err != nil {
		log("[读取文件错误]:" + err.Error())
	}
	hb.HTML.Md = string(con)
	return hb
}

// - 加载自定义组件结构
/*
 * 该模式采用优先使用页面YAML配置,最后补充默认配置
 * 1. 设置HTMLhead部分 title keywords description
 * 2. 加载三方link样式
 * 3. 自定义语法替换成对应标签
 */
func (hb *HTMLBuiler) readYAML() *HTMLBuiler {
	reg := regexp.MustCompile("---([\\s\\S]*?)---[\\s]?")
	matches := reg.FindAllString(hb.HTML.Md, -1)
	// log(hb.HTML.IFile)
	for _, v := range matches {
		s := strings.ReplaceAll(v, "---", "")
		err := yaml.NewDecoder(strings.NewReader(s)).Decode(&hb.HTML.HF)
		if err != nil {
			err.Error()
			// CopyStruct(hf, &hb.HTML.HF)
		}
		// log(hb.HTML.HF.Title)
		if len(hb.HTML.HF.Style) > 0 { // - 加载配置样式
			for _, st := range hb.HTML.HF.Style {
				hb.HF.Style = append(hb.HTML.HF.Style, st)
			}
		}
		if len(hb.HTML.HF.Script) > 0 { // - 加载配置JS
			for _, sr := range hb.HTML.HF.Script {
				hb.HF.Script = append(hb.HTML.HF.Script, sr)
			}
		}
	}
	hb.HTML.Md = reg.ReplaceAllString(hb.HTML.Md, "")
	return hb
}

/*
 * 支持MarkDown流程图 ```mermaid 	``` 转化成<div class="mermaid"></div>
 */
func (hb *HTMLBuiler) mermaid() *HTMLBuiler {
	reg := regexp.MustCompile("\\`\\`\\`(.*?)mermaid([\\s\\S]*?)\\`\\`\\`[\\s]?")
	matches := reg.FindAllString(hb.HTML.Md, -1)
	for _, v := range matches {
		le := Replace(v, "\\`\\`\\`(.*?)mermaid", `<div class="mermaid">`)
		le = Replace(le, "\\`\\`\\`", "</div>")
		hb.HTML.Md = strings.Replace(hb.HTML.Md, v, le, -1)
	}
	return hb
}

/*
 * 支持MarkDown流程图 ```mermaid 	``` 转化成<div class="mermaid"></div>
 */
func (hb *HTMLBuiler) slot() *HTMLBuiler {
	reg := regexp.MustCompile(":::(.*?)(success|error|warning)([\\s\\S]*?):::[\\s]?")
	matches := reg.FindAllString(hb.HTML.Md, -1)
	for _, v := range matches {
		le := Replace(v, ":::(.*?)success", `<div class="success">`)
		le = Replace(le, ":::(.*?)error", `<div class="error">`)
		le = Replace(le, ":::(.*?)warning", `<div class="warning">`)
		le = Replace(le, ":::(.*?)page", `<div class="page">`)
		le = strings.Replace(le, ":::", "</div>", -1)
		hb.HTML.Md = strings.Replace(hb.HTML.Md, v, le, -1)
	}
	return hb
}

/*
 * todo代做标签
 */
func (hb *HTMLBuiler) todo() *HTMLBuiler {
	hb.HTML.Md = strings.Replace(hb.HTML.Md, "[ ]", `<i class="checkbox"></i>`, -1)
	hb.HTML.Md = strings.Replace(hb.HTML.Md, "[x]", `<i class="checkbox checked" ></i>`, -1)
	return hb
}

/*
 * 将当前页面Markdown转化成Html
 */

func (hb *HTMLBuiler) html() *HTMLBuiler {
	unsafe := blackfriday.MarkdownCommon([]byte(hb.HTML.Md))
	hb.HTML.Md = string(unsafe)
	return hb
}

/*
 * 检索当前所有标题标签生成ul-ol列表
 */

func (hb *HTMLBuiler) list() *HTMLBuiler {
	reg := regexp.MustCompile("<h[1-6]>([\\s\\S]*?)</h[1-6]>[\\s]?")
	matches := reg.FindAllString(hb.HTML.Md, -1)
	hb.HTML.List = "<ul>@{o-1}</ul>"
	// TODO:将顺序列表转化成ul-ol嵌套关系
	for k, v := range matches {
		/*
		 -将h1标签转化成ol标签,并且留有
		 ! <ul>@{ol}</ul>
		 - 作为插槽供 h1下的h2标签归入到h1的ol中
		*/
		switch v[1:3] {
		case "h1":
			if k == 0 { // 第一个元素是h1既标题
				hb.HTML.Title = getTagContent(v, "h1")
			}
			hb._list(1, v)
		case "h2":
			hb._list(2, v)
		case "h3":
			hb._list(3, v)
		}
	}
	// - 去除列表中的  <ul>@{o-1}</ul>
	unReg := regexp.MustCompile("@{o-[1-7]}")
	ms := unReg.FindAllString(hb.HTML.List, -1)
	for _, r := range ms {
		hb.HTML.List = strings.Replace(hb.HTML.List, r, "", 1)
	}
	hb.HTML.List = strings.Replace(hb.HTML.List, "<ul></ul>", "", -1)
	return hb
}
func (hb *HTMLBuiler) _list(id int, v string) {
	v = strings.Replace(v, "\n", "", -1)
	hb.HTML.List = strings.Replace(hb.HTML.List, `<ul>@{o-`+strconv.Itoa(id+1)+`}</ul>`, "", 1)
	t := strings.Replace(strings.Replace(v, `<h`+strconv.Itoa(id)+`>`, "", 1), `</h`+strconv.Itoa(id)+`>`, "", 1) //剥除h标签
	ol := strings.Replace(v, `<h`+strconv.Itoa(id)+`>`, `<ol class="o-`+strconv.Itoa(id)+`"><span href="#`+t+`">`, 1)
	ol = strings.Replace(ol, `</h`+strconv.Itoa(id)+`>`, `</span><ul>@{o-`+strconv.Itoa(id+1)+`}</ul></ol>@{o-`+strconv.Itoa(id)+`}`, 1)
	hb.HTML.List = strings.Replace(hb.HTML.List, `@{o-`+strconv.Itoa(id)+`}`, ol, 1)
}

/*
 * 检索当前页面是否有toc目录索引符号,存在获取当前页面标题
 */

func (hb *HTMLBuiler) toc() *HTMLBuiler {
	idx := strings.Index(hb.HTML.Md, "[[toc]]")
	if idx >= 0 {
		hb.HTML.Md = strings.Replace(hb.HTML.Md, "[[toc]]", hb.HTML.List, 1)
	}
	return hb
}

func (hb *HTMLBuiler) defaultConfig() *HTMLBuiler {
	// log(strconv.Itoa(len(hb.HTML.HF.Title)) + ":" + hb.HTML.HF.Title)

	if len(hb.HTML.HF.Title) == 0 {
		hb.HTML.HF.Title = getWebSiteByKey("title")
	}

	if len(hb.HTML.HF.Lang) == 0 {
		hb.HTML.HF.Lang = getWebSiteByKey("lang")
	}
	if len(hb.HTML.HF.KeyWords) == 0 {
		hb.HTML.HF.KeyWords = getWebSiteByKey("keywords")
	}
	if len(hb.HTML.HF.Description) == 0 {
		hb.HTML.HF.Description = getWebSiteByKey("description")
	}
	if len(hb.HTML.HF.Ico) == 0 {
		hb.HTML.HF.Ico = getWebSiteByKey("ico")
	}
	if !hb.HTML.HF.Slide {
		hb.HTML.HF.Slide = StrToBool(getWebSiteByKey("slide"))
	}
	stl := getWebSiteByKey("style")
	for _, v := range strings.Split(stl, ",") {
		hb.HF.Style = append(hb.HF.Style, v)
	}
	scr := getWebSiteByKey("script")
	for _, v := range strings.Split(scr, ",") {
		hb.HF.Script = append(hb.HF.Script, v)
	}
	return hb
}
